<template>
  <div class="vix-list-header vix-filter-inline">
    <u-form :model="queryData" layout="inline">
      <u-form-item label="角色名称" prop="roleName">
        <u-input
          v-model="queryData.roleName"
          style="width: 200px"
          placeholder="请输入角色名称"
          clearable
        />
      </u-form-item>
      <u-button
        type="primary"
        :loading="listQuery.loading"
        @click="handleSearch"
      >
        查询
      </u-button>
    </u-form>
  </div>
  <div class="vix-list-body">
    <div class="vix-operate-wrap">
      <div class="vix-operate-section vix-operate-section-right t-right">
        <u-button type="text" class="vix-operate-button" @click="handleAdd">
          <u-icon type="add-circle-outline" />新建角色
        </u-button>
      </div>
    </div>
    <div class="list-content">
      <u-table
        ref="table"
        :columns="columns"
        :data="listData"
        :loading="listQuery.loading"
        :border="false"
        row-key="id"
        show-pagination
        :pagination="{
          total: listQuery.total,
          current: listQuery.currentPage,
          pageSize: listQuery.pageSize,
          showSizer: true,
          showTotal: true
        }"
        @change="handleTableChange"
      >
        <template #createTime="{ row }">
          {{ parseTime(row.createTime, 'YYYY-MM-DD HH:mm:ss') }}
        </template>
        <template #roleStatus="{ row }">
          <span
            class="status-color"
            :class="
              getEnumDisplay(row.roleStatus, statusOptions, {
                value: 'value',
                label: 'class'
              })
            "
          >
            {{ getEnumDisplay(row.roleStatus, statusOptions) }}
          </span>
        </template>
        <template #action="{ row }">
          <u-tooltip
            :title="computDisabledEdit(row) ? '角色被锁定不可编辑' : '编辑'"
          >
            <u-link
              type="primary"
              :underline="false"
              :disabled="computDisabledEdit(row) || undefined"
              @click="handleEdit(row)"
            >
              <u-icon type="edit" :size="14" />
            </u-link>
          </u-tooltip>

          <u-tooltip
            :title="'角色被锁定不可删除'"
            :disabled="!computDisabledEdit(row)"
          >
            <span>
              <u-pop-confirm
                title="确认删除吗?"
                :disabled="computDisabledEdit(row)"
                @confirm="handleDelete(row)"
              >
                <u-link
                  type="primary"
                  :underline="false"
                  class="m-l-1"
                  :disabled="computDisabledEdit(row)"
                >
                  <u-icon type="delete" :size="14" />
                </u-link>
              </u-pop-confirm>
            </span>
          </u-tooltip>
        </template>
      </u-table>
    </div>
  </div>
  <edit-drawer
    v-model:visible="editShow"
    :title="editTitle"
    show-submit
    :submit-loading="saveLoading"
    @submit="handleSave"
    @close="handleClose"
    @cancel="handleClose"
  >
    <dynamics-form ref="formRef" :model="editData" :setting="editSetting">
      <template #auth>
        <u-spin v-if="treeLoading">
          <u-icon type="loading" :size="18" />
          <span>加载中...</span>
        </u-spin>

        <div v-else class="auth-data-container">
          <u-tree
            ref="tree"
            v-model:checkedKeys="editData.authority"
            v-model:halfCheckedKeys="indeterminateKeys"
            :tree-data="treeData"
            checkable
          >
            <template #title="{ node }">
              <u-tag
                class="menu-tree-tag"
                :color="
                  getEnumDisplay(node.category, authCategoryOptions, {
                    value: 'value',
                    label: 'color'
                  })
                "
              >
                {{ getEnumDisplay(node.category, authCategoryOptions) }}
              </u-tag>
              <span>{{ node.name }}</span>
            </template>
          </u-tree>
        </div>
      </template>
    </dynamics-form>
  </edit-drawer>
</template>

<script>
import { ref, reactive, computed } from 'vue'
import {
  DynamicsForm, EditDrawer, hooks, libs, message
} from '@bairong/vix'
import {
  getRoleListApi,
  getRoleTypeResourceApi,
  updateRoleApi,
  addRoleApi,
  deleteRoleApi,
  checkRoleNameApi
} from '@api/author'
import {
  authCategoryOptions,
  yesOrNoOptions,
  statusOptions
} from '@/libs/data/enums'

const { getEnumDisplay, parseTime } = libs.conversion
const { usePageList, usePermission } = hooks

export default {
  name: 'RoleManage',
  components: {
    DynamicsForm,
    EditDrawer
  },
  setup() {
    const { hasAuth } = usePermission()
    // ======  list  =======
    // #region
    const queryData = reactive({
      roleName: ''
    })

    const columns = ref([
      {
        title: '角色名称',
        minWidth: 100,
        key: 'roleName'
      },
      {
        title: '角色类型',
        minWidth: 100,
        key: 'roleType',
        slot: 'roleType'
      },
      {
        title: '创建时间',
        minWidth: 150,
        key: 'createTime',
        slot: 'createTime'
      },
      {
        title: '角色状态',
        minWidth: 100,
        key: 'roleStatus',
        slot: 'roleStatus'
      },
      {
        title: '备注',
        align: 'center',
        minWidth: 120,
        tooltip: true,
        key: 'remark'
      },
      {
        title: '操作',
        width: 90,
        key: 'action',
        slot: 'action'
      }
    ])

    const {
      listQuery,
      listData,
      getList,
      handleQuery,
      handlePageChange
    } = usePageList({
      getListApi: getRoleListApi
    })

    getList()

    const handleTableChange = pagination => {
      handlePageChange(pagination.current, pagination.pageSize)
    }

    const handleSearch = () => {
      handleQuery(queryData)
    }

    const handleDelete = row => {
      const { id } = row
      deleteRoleApi({ id })
        .then(() => {
          message.success('删除成功')
          getList(true)
        })
        .catch(err => {
          message.error(err)
        })
    }

    const computDisabledEdit = row => (hasAuth('develop') ? false : row.lockOrNot === 1)

    // #endregion

    // ======= edit =======
    // #region
    const formRef = ref(null)
    const editShow = ref(false)
    const operateType = ref('add')
    const originName = ref('')
    const roleTypeOptions = ref([])
    const authData = ref([])
    const treeData = ref([])
    const treeLoading = ref(false)
    const indeterminateKeys = ref([])

    const editTitle = computed(() => (operateType.value === 'add' ? '新建角色' : '编辑角色'))

    const initModel = () => ({
      name: '',
      remark: '',
      lockOrNot: 0,
      roleStatus: 1,
      roleType: 99,
      authority: []
    })

    const editData = ref(initModel())
    // #region 权限树
    // 递归生成children
    const deepBuildTree = (node, level = 2) => {
      const children = authData.value
        .filter(x => x.parentId === node.id)
        .map(x => ({
          ...x,
          key: x.id,
          level
        }))
        .sort((a, b) => a.sort - b.sort)
      node.children = children
      node.children.forEach(n => {
        deepBuildTree(n, level + 1)
      })
    }
    // 转换树结构数据
    const buildTree = () => {
      const lv1 = authData.value
        .filter(x => x.parentId === 0)
        .map(x => ({
          ...x,
          key: x.id,
          level: 1
        }))
        .sort((a, b) => a.sort - b.sort)
      lv1.forEach(n => {
        deepBuildTree(n)
      })
      const data = lv1 || []
      treeData.value = data
    }
    // 根据角色类型。获取权限列表
    const getAuthData = () => {
      treeLoading.value = true
      const { roleType } = editData.value
      const params = {
        code: roleType
      }
      return getRoleTypeResourceApi(params).then(data => {
        treeLoading.value = false
        authData.value = data
        buildTree()
      })
    }
    // #endregion

    // #region editSetting
    const validateRoleName = (rule, value, callback) => {
      const pattern = /^[\u4e00-\u9fa5a-zA-Z0-9_]{2,20}$/.test(value)
      if (value && value === originName.value) {
        callback()
      } else if (pattern) {
        checkRoleNameApi({ key: value })
          .then(res => {
            if (res) {
              callback()
            } else {
              callback(new Error('角色名称已存在'))
            }
          })
          .catch(err => {
            callback(new Error(err))
          })
      } else {
        callback(new Error('角色名称仅支持2-20位中文、字母、数字、下划线'))
      }
    }
    const editSetting = ref([
      {
        name: 'roleName',
        label: '角色名称',
        placeholder: '请输入角色名称',
        type: 'input',
        rules: [
          { required: true, message: '请填写角色名称', trigger: 'blur' },
          {
            validator: validateRoleName,
            trigger: 'blur'
          }
        ]
      },
      {
        name: 'remark',
        label: '备注',
        placeholder: '请输入备注',
        props: {
          maxlength: 100,
          rows: 2
        },
        type: 'textarea'
      },
      {
        name: 'lockOrNot',
        label: '是否锁死角色',
        type: 'radioGroup',
        show: () => hasAuth('develop'),
        options: yesOrNoOptions
      },
      {
        name: 'roleStatus',
        label: '角色状态',
        type: 'radioGroup',
        options: statusOptions
      },
      {
        name: 'authority',
        label: '权限',
        type: 'slot',
        slot: 'auth',
        rule: [{ required: true, type: 'array', message: '请配置权限' }]
      }
    ])
    // #endregion

    // #region  event
    const handleAdd = () => {
      operateType.value = 'add'
      editData.value = initModel()

      getAuthData()
      editShow.value = true
    }

    const getCheckedKeys = authority => {
      const allkeys = authority.split(',').map(x => parseInt(x, 10))
      const checkedKeys = []
      allkeys.forEach(key => {
        if (!authData.value.some(x => x.parentId === key)) {
          checkedKeys.push(key)
        }
      })
      return checkedKeys
    }

    const handleEdit = row => {
      operateType.value = 'edit'

      const {
        id,
        roleName,
        roleStatus,
        lockOrNot,
        remark,
        authority
      } = row
      originName.value = roleName
      editData.value = {
        id,
        roleName,
        roleStatus,
        lockOrNot,
        remark,
        authority: []
      }
      getAuthData().then(() => {
        editData.value.authority = getCheckedKeys(authority)
      })
      editShow.value = true
    }

    const saveLoading = ref(false)

    const handleSave = () => {
      formRef.value.validate().then(() => {
        saveLoading.value = true
        const {
          id,
          roleName,
          roleStatus,
          lockOrNot,
          remark,
          authority
        } = editData.value
        const params = {
          id,
          roleName,
          roleStatus,
          lockOrNot,
          remark,
          authority: [...authority, ...indeterminateKeys.value].join()
        };
        (operateType.value === 'add' ? addRoleApi : updateRoleApi)(params)
          .then(() => {
            saveLoading.value = false
            editShow.value = false
            message.success('保存成功')
            getList()
          })
          .catch(err => {
            saveLoading.value = false
            message.error(err)
          })
      })
    }

    const handleClose = () => {
      formRef.value.resetFields()
      editShow.value = false
    }
    // #endregion
    // #endregion

    return {
      queryData,
      columns,
      listQuery,
      listData,
      handleTableChange,
      handleSearch,
      handleDelete,
      computDisabledEdit,
      formRef,
      editShow,
      editTitle,
      editData,
      editSetting,
      treeData,
      indeterminateKeys,
      treeLoading,
      saveLoading,
      handleAdd,
      handleEdit,
      handleSave,
      handleClose,
      getEnumDisplay,
      parseTime,
      authCategoryOptions,
      statusOptions,
      roleTypeOptions
    }
  }
}
</script>
